#include <iostream>
#include<assert.h>
#include<unistd.h>
#include<string>
#include <cassert>
#include<sys/socket.h>
#include<arpa/inet.h>  //字节序转换接口头文件
#include<netinet/in.h>  //地址结构类型以及协议类型宏头文件

//进行 UDP 接口的封装

class UdpSocket{
  private:
    int _sockfd;
  public:
    UdpSocket():_sockfd(-1) {}
    ~UdpSocket() {
      Close();
    }

    //创建套接字
    bool Socket(){
      _sockfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);//定义为 IPV4 地址域，数据报传输，定义传输协议为 UDP 协议
      if(_sockfd<0){
        perror("socket error!\n");
        return false;  
      }
      return true;  //创建套接字成功
    }

    //给套接字绑定地址空间
    bool Bind(const std::string& ip,uint16_t port)
    {
      //创建 ipv4 地址域结构
      struct sockaddr_in addr;
      addr.sin_family=AF_INET;  //ipv4 地址域类型
      addr.sin_port=htons(port);  //将主机字节序转换为网络字节序---16位使用 htons   
      addr.sin_addr.s_addr=inet_addr(ip.c_str());  //将 IP 地址转换为整型 IP 
      socklen_t len=sizeof(struct sockaddr_in);

      int ret=bind(_sockfd,(struct sockaddr*)&addr,len);
      if(ret<0){
        perror("bind error!\n");
        return false;
      }
      return true;
    }

    //接收数据
    bool Recv(std::string& body,std::string* peer_ip = NULL,uint16_t* peer_port=NULL)
    {
      struct sockaddr_in peer;
      socklen_t len=sizeof(struct sockaddr_in);
      char tmp[4096]={0};
      ssize_t ret=recvfrom(_sockfd,tmp,4096,0,(struct sockaddr*)&peer,&len);
      if(ret<0){
        perror("recvfrom error!\n");
        return false;
      }
      if(peer_ip!=NULL)
        *peer_ip=inet_ntoa(peer.sin_addr);  //获取到源端IP信息
      if(peer_port!=NULL)
        *peer_port=ntohs(peer.sin_port);  //将网络套接字转换为主机套接字
      body.assign(tmp,len);  //从 tmp 获取 len 长度字节放入到 body 中
      return true;
    }


    //发送数据
    bool Send(const std::string& body,const std::string& peer_ip,uint16_t peer_port)
    {
      struct sockaddr_in addr;
      addr.sin_family=AF_INET;
      addr.sin_port=htons(peer_port);  //将点云十进制 IP 转换为整型 IP
      addr.sin_addr.s_addr=inet_addr(peer_ip.c_str());  //将主机字节序转换为网络字节序
      socklen_t len=sizeof(struct sockaddr_in);

      ssize_t ret=sendto(_sockfd,body.c_str(),body.size(),0,(struct sockaddr*)&addr,len);
      if(ret<0){
        perror("sendto error!\n");
        return false;
      }
      return true;
    }

    //关闭套接字
    bool Close()
    {
      if(_sockfd!=-1){
        close(_sockfd);
        _sockfd=-1;
      }
      return true;
    }
};

